using System;
using System.Diagnostics.Contracts;
using System.Linq;

namespace OpenWaves
{
    public static partial class UrlBuilderExtensions
    {
        public static T SetParameter<T>(this T builder, string parameterName, object parameterValue) where T : UrlBuilder
        {
            Contract.Requires<ArgumentNullException>(builder != null);
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(parameterName) == false);
            Contract.Requires<ArgumentNullException>(parameterName != null);
            Contract.Ensures(Contract.Result<T>() != null);

            builder.Query[parameterName] =  parameterValue;
            return builder;
        }

        public static T AddParameter<T>(this T builder, string parameterName, object parameterValue) where T : UrlBuilder
        {
            Contract.Requires<ArgumentNullException>(builder != null);
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(parameterName) == false);
            Contract.Requires<ArgumentNullException>(parameterName != null);
            Contract.Ensures(Contract.Result<T>() != null);
            
            builder.Query.Add(parameterName, parameterValue);
            return builder;
        }

        public static T RemoveParameter<T>(this T builder, string parameterName) where T : UrlBuilder
        {
            Contract.Requires<ArgumentNullException>(builder != null);
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(parameterName) == false);
            Contract.Ensures(Contract.Result<T>() != null);

            builder.Query.Remove(parameterName);
            return builder;
        }

        public static T SetPath<T>(this T builder, UrlPath path) where T : UrlBuilder
        {
            Contract.Requires<ArgumentNullException>(builder != null);
            Contract.Requires<ArgumentNullException>(path != null);
            Contract.Ensures(Contract.Result<T>() != null);
            
            builder.Path = path;
            return builder;
        }

        public static T AppendPathSegments<T>(this T builder, params string[] newSegments) where T : UrlBuilder
        {
            Contract.Requires<ArgumentNullException>(builder != null);
            Contract.Requires<ArgumentNullException>(newSegments != null);
            Contract.Ensures(Contract.Result<T>() != null);
            
            var segments = builder.Path.Segments.ToList();
            segments.AddRange(newSegments);
            builder.Path = new UrlPath(segments, builder.Path.IsAbsolute);
            return builder;
        }

        public static T SetLastSegment<T>(this T builder, string segment) where T : UrlBuilder
        {
            Contract.Requires<ArgumentNullException>(builder != null);
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(segment) == false);
            Contract.Ensures(Contract.Result<T>() != null);
            
            var segments = builder.Path.Segments.ToList();
            if (segments.Count > 0)
                segments[segments.Count - 1] = segment;
            else
                segments.Add(segment);

            builder.Path = new UrlPath(segments, builder.Path.IsAbsolute);
            return builder;
        }

        public static T ClearPath<T>(this T builder) where T : UrlBuilder
        {
            Contract.Requires<ArgumentNullException>(builder != null);
            Contract.Ensures(Contract.Result<T>() != null);

            builder.Path = UrlPath.Empty;
            return builder;
        }

        public static T SetFragment<T>(this T builder, string fragment) where T : UrlBuilder
        {
            Contract.Requires<ArgumentNullException>(builder != null);
            Contract.Requires<ArgumentNullException>(fragment != null);
            Contract.Ensures(Contract.Result<T>() != null);

            builder.Fragment = fragment;
            return builder;
        }

        public static T SetQuery<T>(this T builder, string query) where T : UrlBuilder
        {
            Contract.Requires<ArgumentNullException>(builder != null);
            Contract.Requires<ArgumentNullException>(query != null);
            Contract.Ensures(Contract.Result<T>() != null);

            builder.Query.Clear();
            foreach (var parameter in UrlQueryCollection.Parse(query))
            {
                builder.Query.Add(parameter.Key, parameter.Value);
            }
            return builder;
        }

        public static T ClearQuery<T>(this T builder) where T : UrlBuilder
        {
            Contract.Requires<ArgumentNullException>(builder != null);
            Contract.Ensures(Contract.Result<T>() != null);

            builder.Query.Clear();
            return builder;
        }
    }
}